%%%-------------------------------------------------------------------
%%% @author liuwentao
%%% @doc
%%%         角色进程
%%% @end
%%% Created : 08. 7月 2021 16:33
%%%-------------------------------------------------------------------
-module(role).
-author("liuwentao").

-behaviour(gen_server).

-export([
    start/1
    , stop/3
    , rpc/3
    , rpc/5
    , disconnect/2
    , pack_send/2
    , pack_send/3
    , send/2
    , link_send/2
    , link_send/3
    , apply/3
    , convert/2
    , element/2
    , send_buff_begin/0
    , send_buff_flush/0
    , send_buff_clean/0
    , send_buff_print/1
    , update_all_power/1
    , get_max_all_power/0
    , update_role_power/1
    , get_max_role_power/0
    , get_dict/1
    , get_dict_int/1
    , get_dict/2
    , put_dict/2
    , put_dict/3
    , erase_dict/1
    , erase_dict/2
    , get_proc_name/1
    , pack/2
]
).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-include("common.hrl").
-include("role.hrl").
-include("protocol.hrl").
-include("conn.hrl").


%% 角色单次操作信息缓存 用于日志处理等
-define(role_cache_init,
    put(rpc_code, 0),
    erase(fun_call_transaction),
    erase(evt_transaction)
).

%% 需要检测role返回值的模块
-define(role_check_return, [
    role_data
]).

%% ----------------------------------------------------
%% 对外接口
%% ----------------------------------------------------

%% @doc 创建一个角色进程
%% <div>创建的角色进程具有全服唯一的进程名称</div>
-spec start(Role :: #role{}) -> {ok, pid()} | ignore | {error, term()}.
start(#role{id = RoleId} = Role) ->
    %% 注册成本地进程,防止同一个角色同时登录可能创建了两个进程(如果前一个登录的mysql里面耽误时间了 就可能还没start完,后面又来了一个登录请求,就会创建两个进程).
    LocalName = get_proc_name(RoleId),
    gen_server:start({local, LocalName}, ?MODULE, [Role], []).

%% @doc 强制终止角色进程的运行
%% <div>注意:如果Msg为非空则需要等Msg发送完成后才返回，当玩家网络不好时，这个时间可能会比较长</div>
-spec stop(async | sync, pid(), bitstring()) -> ok.
stop(async, RolePid, Msg) when is_pid(RolePid) ->
    RolePid ! {stop, Msg},
    ok;
stop(sync, RolePid, Msg) when is_pid(RolePid) ->
    ?CALL(RolePid, {stop, Msg});
stop(_, _, _) ->
    ok.

%% @doc 连接器断开通知
disconnect(RolePid, Reason) ->
    RolePid ! {disconnect, Reason},
    ok.

%% @doc 服务端调用接口(模拟客户端调用),测试接口,该接口不应该在正式逻辑里面用!
-spec rpc(RolePid :: pid(), Code :: pos_integer(), Data :: tuple()) -> ok.
rpc(RolePid, Code, Data) ->
    case mapping:module(game_server, Code) of
        {ok, _Auth, _Caller, Proto, Mod} ->
            case Proto:pack(cli, Code, Data) of
                {ok, <<_A1:32, _A2:16, Bin/binary>>} ->
                    RolePid ! {rpc, srv, Proto, Mod, Code, Bin};
                {error, Reason} ->
                    ?ERR("打包数据出错[Code:~w][Reason:~w]", [Code, Reason])
            end;
        {error, _Code} ->
            ?ERR("模块影射失败[~w]:~w", [Code, Data])
    end.

%% @doc 客户端调用接口(socket事件处理)
-spec rpc(RolePid, Parser, Mod, Code, Bin) -> ok when
    RolePid :: pid(),
    Parser :: atom(),
    Mod :: atom(),
    Code :: pos_integer(),
    Bin :: binary().
rpc(RolePid, Parser, Mod, Code, Bin) ->
    RolePid ! {rpc, cli, Parser, Mod, Code, Bin},
    ok.

%% @doc 对指定角色应用MFA(可选择同步或异步方式)
%% <div>注意:使用异步方式时，F的返回值格式要求: F([Role | A]) -> ok | {ok, NewRole}，且调用者只会收到ok的返回值</div>
%% <div>注意:使用同步方式时，F的返回值格式要求: F([Role | A]) -> {ok, Reply} | {ok, Reply, NewRole}，且调用者会收到F的执行结果Reply</div>
-spec apply(T, RolePid, Mfa) -> ok | {error, not_online} | {error, not_pid} | {error, self_call} | Reply when
    T :: async | sync,
    RolePid :: pid(),
    Mfa :: {F} | {F, A} | {M, F, A},
    M :: atom(),
    F :: function(),
    A :: list(),
    Reply :: term().
apply(T, {Rid, P, Z}, Mfa) ->

    case role_query:pid(by_id, Rid, P, Z) of
        {ok, Pid} -> ?MODULE:apply(T, Pid, Mfa);
        _ -> {error, not_online}
    end;
apply(_T, RolePid, _Mfa) when not is_pid(RolePid) ->
    {error, not_pid};
apply(async, RolePid, {F}) ->
    RolePid ! {apply_async, {F}},
    ok;
apply(async, RolePid, {F, A}) ->
    RolePid ! {apply_async, {F, A}},
    ok;
apply(async, RolePid, {M, F, A}) ->
    RolePid ! {apply_async, {M, F, A}},
    ok;
apply(sync, RolePid, Mfa) when self() =:= RolePid ->
    {M, F, _, _} = hd(tl(erlang:get_stacktrace())),
    ?ERR("调用者[~w:~w]执行apply[~w]时发生错误：调用了自身, stack ~w", [M, F, Mfa, util:get_stacktrace()]),
    {error, self_call};
apply(sync, RolePid, {F}) ->
    ?CALL(RolePid, {apply_sync, F});
apply(sync, RolePid, {F, A}) ->
    ?CALL(RolePid, {apply_sync, {F, A}});
apply(sync, RolePid, {M, F, A}) ->
    ?CALL(RolePid, {apply_sync, {M, F, A}}).

%% @doc 将角色转换为指定的数据类型
%% <div>此接口在跨节点访问时可以节省大量的数据传输，非必要时不要跨节点传输整个#role{}数据</div>
-spec convert(RolePid, Type) -> {ok, tuple()} | {error, timeout} | {error, noproc} | {error, term()} when
    RolePid :: pid(),
    Type :: atom().
convert(RolePid, _Type) when self() =:= RolePid ->
    {M, F, _, _} = hd(tl(erlang:get_stacktrace())),
    ?ERR("调用者[~w:~w]执行role:convert/2时发生错误：调用了自身, stack ~w", [M, F, util:get_stacktrace()]),
    {error, self_call};
convert(RolePid, Type) ->
    ?CALL(RolePid, {convert, Type}).

%% @doc 获取指定角色的某些属性
%% <div>示例:[Event, Status] = role:element(RolePid, [#role.event, #role.status])</div>
-spec element(RolePid, ElemPos) -> {ok, [term()]} | {error, self_call} when
    RolePid :: pid(),
    ElemPos :: [pos_integer()].
element(RolePid, _ElemPos) when self() =:= RolePid ->
    {M, F, _, _} = hd(tl(erlang:get_stacktrace())),
    ?ERR("调用者[~w:~w]执行role:element/2时发生错误：调用了自身, stack ~w", [M, F, util:get_stacktrace()]),
    {error, self_call};
element(RolePid, ElemPos) when is_list(ElemPos) ->
    ?CALL(RolePid, {element, ElemPos}).

%% @doc 开启发送缓冲区(需要进行事务性处理时使用)
%% <div>注意:send_buff_begin/0必须跟send_buff_flush/0或send_buff_clean/0配对使用</div>
%% <div>否则将会导致数据一直发送到缓冲区，因此客户端会一直收不到数据</div>
%% <div>此函数支持嵌套使用，只是必须保证成对出现</div>
%% <div>使用发送缓冲区会增加少量的性能消耗，确定是否有必要使用</div>
-spec send_buff_begin() -> ok.
send_buff_begin() ->
    case get(is_role_process) of
        true ->
            case get(evt_transaction) of
                undefined -> put(evt_transaction, []);
                _ -> ok
            end,
            put(send_buff, [[] | get(send_buff)]);
        _ ->
            ?ERR("不能在非角色进程内使用发送缓冲区"),
            ignore
    end,
    fun_call:trans_begin(),
    ok.

%% @doc 立即发送缓冲区中的数据并关闭缓冲区(只会关闭一层)
-spec send_buff_flush() -> ok.
send_buff_flush() ->
    case get(send_buff) of
        [] -> ?ERR("发送缓冲区数据时发生异常:缓冲区为空");
        [[]] -> %% 没有数据，不需要发送
            put(send_buff, []);
        [H] -> %% 已经是最后一层的事务了，可以直接发送
            get(conn_pid) ! {send_data, lists:reverse(H)}, %% 注意:必须直接发送到连接器
            put(send_buff, []);
        [H, H1 | T] ->
            put(send_buff, [[lists:reverse(H) | H1] | T]);
        [H | T] ->
            put(send_buff, [[lists:reverse(H) | T]])
    end,
    fun_call:trans_flush(),
    ok.

%% @doc 清空发送缓冲区并关闭缓冲区(只会关闭一层)
-spec send_buff_clean() -> ok.
send_buff_clean() ->
    case get(send_buff) of
        [] -> ?ERR("清空缓冲区数据时发生异常:缓冲区为空");
        [_H] ->
            erase(evt_transaction),
            put(send_buff, []);
        [_H | T] ->
            put(send_buff, T)
    end,
    fun_call:trans_clean(),
    ok.

-spec send_buff_print(_) -> ok.
send_buff_print(_) ->
    case get(send_buff) of
        [] ->
            ?DEBUG("缓存数据为空"),
            ok;
        _H = [_ | _] -> ?DEBUG("缓存数据为:~w", [_H]), ok;
        _ ->
            ?DEBUG("缓存数据为空"),
            ok
    end.


%% @doc 打包并发送消息到角色进程(由角色进程转发到连接器进程)
%% <div>出于性能考虑，只能用于无法直接获取连接器Pid时使用</div>
-spec pack_send(RolePid, Code, Data) -> ok when
    RolePid :: pid(),
    Code :: pos_integer(),
    Data :: tuple().
pack_send(#role{pid = Pid}, Code, Data) ->
    pack_send(Pid, Code, Data);
pack_send(RolePid, Code, Data) when is_pid(RolePid) ->
    case mapping:module(game_server, Code) of
        {ok, _Auth, _Caller, Proto, _ModName} ->
            case Proto:pack(srv, Code, Data) of
                {ok, Bin} ->
                    RolePid ! {socket_proxy, Bin},
                    ok;
                {error, Reason} ->
                    ?ERR("打包数据出错[Code:~w][Reason:~w]", [Code, Reason])
            end;
        {error, _Code} ->
            ?ERR("模块影射失败[~w]:~w", [Code, Data])
    end;
pack_send(RoleId = {_, _, _}, Code, Data) ->
    case role_query:pid(by_id, RoleId) of
        {ok, Pid} -> pack_send(Pid, Code, Data);
        _ -> ignore
    end;
pack_send(_Tar, _Code, _Data) ->
    ?DEBUG("无效的数据同步: Target[~w], Code[~w], Data[~w]", [_Tar, _Code, _Data]),
    ok.

-spec pack_send(RolePid, Bin) -> ok | false when
    RolePid :: pid(),
    Bin :: binary().
pack_send(RoleId = {_, _, _}, Bin) when is_binary(Bin) ->
    case role_query:pid(by_id, RoleId) of
        {ok, Pid} -> pack_send(Pid, Bin);
        _ ->
            ignore
    end;
pack_send(RolePid, Bin) when is_binary(Bin) ->
    RolePid ! {socket_proxy, Bin},
    ok;
pack_send(_RolePid, Bin) ->
    ?ERR("打包数据出错[Data:~w], stack ~w", [Bin, util:get_stacktrace()]),
    false.


%% @doc 打包协议
pack(Code, Data) ->
    case mapping:module(game_server, Code) of
        {ok, _Auth, _Caller, Parser, _ModName} ->
            case catch Parser:pack(srv, Code, Data) of
                {ok, Bin} ->
                    Bin;
                Err ->
                    ?ERR("打包数据出错[Code:~w][Err:~w]", [Code, Err]),
                    {error, pack_error}
            end;
        Err2 ->
            ?ERR("打包数据出错[Code:~w][Err2:~w]", [Code, Err2]),
            <<>>
    end.

-spec send(RolePid, Bin) -> ok | false when
    RolePid :: pid(),
    Bin :: binary().
send(RolePid, Bin) when is_binary(Bin) ->
    RolePid ! {socket_proxy, Bin},
    ok;
send(_RolePid, Bin) ->
    ?ERR("打包数据出错[Data:~w]", [Bin]),
    false.

%% ----------------------------------
%% @doc 打包发送消息---能取到角色连接进程
%% 建议使用该方法,少用上面
%% ----------------------------------
-spec link_send(Send, pos_integer(), tuple()) -> ok when
    Send :: pid() | #role{} | #link{}.
link_send(#role{link = #link{conn_pid = ConnPid}}, Code, Data) ->
    link_send(ConnPid, Code, Data);
link_send(#link{conn_pid = ConnPid}, Code, Data) ->
    link_send(ConnPid, Code, Data);
link_send(ConnPid, Code, Data) when is_pid(ConnPid) ->
    server_conn:pack_send(ConnPid, Code, Data).

%% ----------------------------------
%% 该方法只能在角色进程使用
-spec link_send(pos_integer(), tuple()) -> ok.
link_send(Code, Data) ->
    case get(conn_pid) of
        undefined -> ?ERR("无法在非角色进程内调用: ~w", [Code]);
        ConnPid -> link_send(ConnPid, Code, Data)
    end.

%% ----------------------------------------------------
%% 内部处理
%% ----------------------------------------------------

init([#role{id = {Rid, P, Z}, account = Acc, link = Link = #link{conn_pid = ConnPid}, name = Name} = Role]) ->
    put(is_role_process, true), %% 标识下这是一个角色进程
    put(already_handle_stop, false), %% 是否已经执行过退出处理
    put(already_login, false), %% 是否已经执行登陆成功
    put(role_info, {Rid, P, Z, Acc}), %% 将角色帐号信息放入进程字典
    put(conn_pid, ConnPid), %% 缓存连接器进程，以供缓冲区发送时直接调用
    put(send_buff, []),
    put(log_transaction, []),
    process_flag(trap_exit, true),
    link(ConnPid),
    self() ! init,
    ?INFO("角色：~ts登录", [Name]),
    {ok, Role#role{pid = self(), link = Link, loop_counter = 1}}.

%% 执行同步apply操作
handle_call({apply_sync, {F}}, _From, Role) ->
    ?role_cache_init,
    handle_apply_sync_return(catch erlang:apply(F, [Role]), {undefined, F, []}, Role);
handle_call({apply_sync, {F, A}}, _From, Role) ->
    ?role_cache_init,
    handle_apply_sync_return(catch erlang:apply(F, [Role | A]), {undefined, F, A}, Role);
handle_call({apply_sync, {M, F, A}}, _From, Role) ->
    ?role_cache_init,
    handle_apply_sync_return(catch erlang:apply(M, F, [Role | A]), {M, F, A}, Role);

handle_call({convert, to_role}, _From, Role) ->
    {reply, {ok, Role}, Role};
handle_call({convert, Type}, _From, Role) ->
    {reply, role_convert:to(Type, Role), Role};

handle_call({element, Pos}, _From, Role) when is_list(Pos) ->
    {reply, {ok, lists:reverse(do_element(Pos, Role, tuple_size(Role), []))}, Role};

%% 将角色踢下线(同步操作)
handle_call({stop, Msg}, _From, Role) ->
    {stop, normal, do_stop(Role, Msg)};

handle_call(_Request, _From, Role) ->
    {noreply, Role}.

handle_cast(_Msg, Role) ->
    {noreply, Role}.

handle_info(init, Role = #role{lev = Lev}) ->
    ?role_cache_init,
    put(login_lev, Lev),
    role:send_buff_begin(),
    case role_login:do(Role) of
        {error, _Msg} ->
            role:send_buff_clean(),
            {stop, normal, Role};
        {ok, NewRole = #role{}} ->
            role:send_buff_clean(),
            put(already_login, true),
            role_query:sync_index(NewRole),
            {ok, Ginfo} = role_convert:to(member, NewRole),
            role_group:join(world, Ginfo),
            self() ! loop,
            erlang:send_after(10000, self(), delay_init),
            true = check_return(NewRole),
            {noreply, NewRole}
    end;

%% 延迟初始数据
handle_info(delay_init, Role) ->
    case role_login:delay_do(Role) of
        {error, _Msg} ->
            ?ERR("延迟登陆初始出错:~w", [_Msg]),
            {noreply, Role};
        {ok, NewRole = #role{}} ->
            true = check_return(NewRole),
            {noreply, NewRole}
    end;

%% 将角色踢下线(异步操作)
handle_info({stop, Msg}, Role) ->
    {stop, normal, do_stop(Role, Msg)};

%% 内部循环
handle_info(loop, Role = #role{loop_counter = C, link = Link, need_sync = Sync}) ->
    %% 约每隔180秒执行一次GC
    case C rem 9 =:= 0 of
        false -> ignore;
        true ->
            garbage_collect(),
            ok
    end,

    %% 约每隔20秒检查一次数据保存需求
    S = case C rem 2 =:= 0 andalso Sync of
            false -> Sync;
            true ->
                role_data:local_save(cache, Role),
                false
        end,

    %% 约每隔2分种检查连接进程是否堆积过多的消息
    case C rem 6 =:= 0 of
        false -> ignore;
        true ->
            case Link of
                #link{conn_pid = ConnPid} ->
                    case process_info(ConnPid, message_queue_len) of
                        {_, QueueLen} when QueueLen > 10000 ->
                            ?ERR("~ts 连接进程堆积消息过多，断开连接", [Role#role.name]),
                            erlang:exit(ConnPid, kill);
                        _ -> ignore
                    end;
                _ ->
                    ignore
            end
    end,

    %% 20秒后进行下次循环
    erlang:send_after(util:rand(20000, 35000), self(), loop),
    {noreply, Role#role{loop_counter = C + 1, need_sync = S}};

%% 执行异步apply操作
handle_info({apply_async, {F}}, Role) ->
    ?role_cache_init,
    handle_apply_async_return(catch erlang:apply(F, [Role]), {undefined, F, []}, Role);
handle_info({apply_async, {F, A}}, Role) ->
    ?role_cache_init,
    handle_apply_async_return(catch erlang:apply(F, [Role | A]), {undefined, F, A}, Role);
handle_info({apply_async, {M, F, A}}, Role) ->
    ?role_cache_init,
    handle_apply_async_return(catch erlang:apply(M, F, [Role | A]), {M, F, A}, Role);


%% 处理客户端调用
handle_info({rpc, From, Parser, Mod, Code, Bin}, Role = #role{link = #link{conn_pid = ConnPid}}) ->
    NewRole =
        case rpc_cfg:get(Code) of
            {error, _Reason} ->
                ?DEBUG("获取rpc配置时发生错误: ~w", [_Reason]),
                Role;
            Rpc ->
                handle_rpc(Rpc#rpc{parser = Parser, mod = Mod, req = Bin}, Role)
        end,
    true = case check_return(NewRole) of
               true ->
                   true;
               _WHY ->
                   ?ERR("数据被初始化了~w, Mod~w, Code~w", [_WHY, Mod, Code]),
                   _WHY
           end,
    read_next(From, ConnPid, NewRole);

%% 转发数据到连接进程
handle_info({socket_proxy, Bin}, Role = #role{link = #link{conn_pid = ConnPid}}) ->
    server_conn:send(ConnPid, Bin),
    {noreply, Role};

%% 角色正常退出
handle_info(normal_exit, Role = #role{name = _Name}) ->
    ?DEBUG("角色[~ts]请求正常退出", [_Name]),
    self() ! {disconnect, normal_exit},
    %% 断线操作
    {noreply, Role};

%% 连接器进程异常退出，协助处理收尾工作
handle_info({'EXIT', ConnPid, _Why}, Role = #role{link = #link{socket = Socket, conn_pid = ConnPid}}) ->
    ?DEBUG("连接进程断开:~w, 进入退出检测", [_Why]),
    self() ! {disconnect, conn_exit},
    catch gen_tcp:close(Socket),
    {noreply, Role};


%% 处理客户端进程后台一段时间,服务端主动断开客户端的连接
handle_info(back_disconnect, Role = #role{link = #link{conn_pid = _MixPid}}) ->
    {noreply, Role};%% 断线之后,不关闭角色进程,(过一段时间自动关闭)

%% 2: conn_exit 连接进程断开, 检测全部状态,决定待机时间
handle_info({disconnect, _Reason}, Role = #role{}) ->
    {stop, normal, do_stop(Role, "")};


handle_info(_Info, Role = #role{name = Name}) ->
    ?ERR("角色进程[~ts]收到未知消息: ~w", [Name, _Info]),
    {noreply, Role}.

terminate(_Reason, Role = #role{}) ->
    NewRole = case get(already_handle_stop) of
                  true -> Role;
                  _ -> do_stop(Role, "")
              end,
    role_group:leave(world, Role#role.pid),
    role_query:clean(NewRole), %% 清除相关的缓存信息
    case role_data:local_save(to_db, NewRole) of
        ok -> role_data:clean_cache(Role#role.id);
        {error, Err} ->
            ?ERR("角色数据[~ts]同步到数据库时发生异常，请勿清空role_data.dets: ~w", [Role#role.name, Err])
    end,
    ok.

code_change(_OldVsn, Role, _Extra) ->
    {ok, Role}.

%% ----------------------------------------------------
%% 私有函数
%% ----------------------------------------------------

%% 角色进程退出处理
do_stop(Role = #role{name = _Name, link = #link{socket = Socket}}, Msg) ->
    ?DEBUG("[~ts]角色进程退出", [_Name]),
    put(already_handle_stop, true),
    case Msg of
        "" -> skip;
        _ ->
            {ok, Bin} = server_conn:pack(9901, {0, Msg}),
            %% 无视发送错误，注意当玩家网络很差时这里等待时间会比较长
            catch gen_tcp:send(Socket, Bin),
            ok
    end,
    case get(already_login) of
        true ->
            ?role_cache_init,
            role_logout:do(Role);
        _ -> Role
    end.

%% 获取#role{}中的某些属性
do_element([], _Role, _MaxPos, L) -> L;
do_element([H | T], Role, MaxPos, L) when H > 0 andalso H =< MaxPos ->
    do_element(T, Role, MaxPos, [erlang:element(H, Role) | L]);
do_element([H | T], Role, MaxPos, L) ->
    ?ERR("传给role:element/2的参数不正确: ~w", [H]),
    do_element(T, Role, MaxPos, L).

%% 处理客户端调用
handle_rpc(
    #rpc{code = Code, parser = Parser, req = Bin, mod = Mod},
    Role = #role{name = Name, link = #link{conn_pid = ConnPid}}
) ->
    put(rpc_code, Code), %% 记录下当前的命令号
    case catch Parser:unpack(srv, Code, Bin) of
        {ok, Data} ->
            ?role_cache_init,
            case catch Mod:handle(Code, Data, Role) of
                %% 无需回应客户端，且角色数据不更新，也不同步到磁盘和缓存
                ok ->
                    Role;
                {ok} ->
                    Role;
                %% 无需回应客户端，角色数据需更新，但不同步到磁盘和缓存
                {nosync, NewRole} when is_record(NewRole, role) ->
                    %% 角色数据发生变化，进行事件处理
                    NewRole#role{need_sync = true};
                %% 需回应客户端，角色数据也需更新，但不同步到磁盘和缓存
                {nosync, Reply, NewRole} when is_record(NewRole, role) ->
                    %% 角色数据发生变化，进行事件处理
                    server_conn:pack_send(ConnPid, Code, Reply),
                    NewRole#role{need_sync = true};
                %% 无需回应客户端，但角色数据需要更新，且需同步到磁盘和缓存
                {ok, NewRole} when is_record(NewRole, role) ->
                    %% 角色数据发生变化，进行事件处理
                    NewRole#role{need_sync = true};
                %% 需回应客户端，但角色数据无需更新，也无需同步到磁盘和缓存
                {reply, Reply} ->
                    server_conn:pack_send(ConnPid, Code, Reply),
                    Role;
                %% 需回应客户端，角色数据也需更新，且需同步到磁盘和缓存
                {reply, Reply, NewRole} when is_record(NewRole, role) ->
                    %% @todo 角色数据发生变化，进行事件处理
                    server_conn:pack_send(ConnPid, Code, Reply),
                    NewRole#role{need_sync = true};
                {error, unknow_command} ->
                    ?ERR("[~ts]的角色进程处理命令[~w][~w]时出错:unknow_command", [Name, Code, Data]),
                    Role;
                Err ->
                    ?ERR("[~ts]的角色进程处理命令[~w][~w]时出错:~w", [Name, Code, Data, Err]),
                    role:send_buff_clean(),
                    Role
            end;
        _Err ->
            ?DEBUG("解包数据出错:~w", [_Err]),
            Role
    end.

%% 服务端主动发送的,不自动读取
read_next(srv, _ConnPid, Role) ->
    {noreply, Role};
%% 通知连接器读取下一条指令
read_next(_From, ConnPid, Role) ->
    ConnPid ! read_next,
    {noreply, Role}.

%% 处理异步apply的返回值
handle_apply_async_return({ok, NewRole}, _Mfa, _Role) when is_record(NewRole, role) ->
    true = check_return(NewRole),
    {noreply, NewRole};
handle_apply_async_return(ok, _Mfa, Role) ->
    {noreply, Role};
handle_apply_async_return({ok}, _Mfa, Role) ->
    {noreply, Role};
handle_apply_async_return(Else, {M, F, A}, Role) ->
    ?ERR("角色[~ts]执行{~w, ~w, ~w}时得到错误的返回值格式:~w", [Role#role.name, M, F, A, Else]),
    {noreply, Role}.

%% 处理同步apply的返回值
handle_apply_sync_return({ok, Reply, NewRole}, _Mfa, _Role) when is_record(NewRole, role) ->
    true = check_return(NewRole),
    {reply, Reply, NewRole};
handle_apply_sync_return({ok, Reply}, _Mfa, Role) ->
    {reply, Reply, Role};
handle_apply_sync_return(Else, {M, F, A}, Role) ->
    ?ERR("角色[~ts]同步执行{~w, ~w, ~w}时得到错误的返回值格式:~w", [Role#role.name, M, F, A, Else]),
    {reply, Else, Role}.

%% @doc 获取当前角色的进程字典数据
-spec get_dict(term()) -> {ok, term()}|{error, atom()}.
get_dict(Key) ->
    case get('is_role_process') of
        true -> {ok, get(Key)};
        _ ->
            ?ERR("当前进程不是一个角色进程，不能使用get_dict/1"),
            {error, not_a_role_process}
    end.

%% @doc 获取当前角色的进程字典整型数据
-spec get_dict_int(term()) -> integer().
get_dict_int(Key) ->
    case get('is_role_process') of
        true ->
            case get(Key) of
                Val when is_integer(Val) -> Val;
                _ -> 0
            end;
        _ ->
            ?ERR("当前进程不是一个角色进程，不能使用get_dict/1"),
            0
    end.

%% @doc 获取当前角色的进程字典数据
-spec get_dict(pid(), term()) -> {ok, term()}|{error, atom()}.
get_dict(RolePid, _Key) when self() =:= RolePid ->
    {error, self_call};
get_dict(RolePid, Key) ->
    gen_server:call(RolePid, {get_dict, Key}).

%% @doc 写入数据到当前角色的进程字典
-spec put_dict(term(), term()) -> {ok, term()}|{error, atom()}.
put_dict(Key, Val) ->
    case get('is_role_process') of
        true -> {ok, put(Key, Val)};
        _ ->
            ?ERR("当前进程不是一个角色进程，不用使用put_dict/2 Key:~w, self ~w", [Key, self()]),
            {error, not_a_role_process}
    end.

%% @doc 写入数据到指定角色的进程字典
-spec put_dict(pid(), term(), term()) -> {ok, term()}|{error, atom()}.
put_dict(RolePid, _Key, _Val) when self() =:= RolePid ->
    ?ERR("进行put_dict/3操作时调用了自身"),
    {error, self_call};
put_dict(RolePid, Key, Val) ->
    gen_server:call(RolePid, {put_dict, Key, Val}).

%% @doc 清除当前角色的进程字典数据
-spec erase_dict(term()) -> {ok, term()}|{error, atom()}.
erase_dict(Key) ->
    case get('is_role_process') of
        true -> {ok, erase(Key)};
        _ -> {error, not_a_role_process}
    end.

%% @doc 清除指定角色的进程字典数据
-spec erase_dict(pid(), term()) -> {ok, term()}|{error, atom()}.
erase_dict(RolePid, _Key) when self() =:= RolePid ->
    {error, self_call};
erase_dict(RolePid, Key) ->
    gen_server:call(RolePid, {erase_dict, Key}).

%5更新在线最高战力
update_all_power(AllPower) ->
    case role:get_dict_int('@max_all_power') of
        MaxAllPower when MaxAllPower < AllPower ->
            role:put_dict('@max_all_power', AllPower),
            ok;
        _ ->
            ok
    end.

%%获取在线最高战力
get_max_all_power() ->
    role:get_dict_int('@max_all_power').

%%更新主角战力
update_role_power(RolePower) ->
    case role:get_dict_int('@max_role_power') of
        MaxRolePower when MaxRolePower < RolePower ->
            role:put_dict('@max_role_power', RolePower),
            ok;
        _ ->
            ok
    end.

%%获取主角战力
get_max_role_power() ->
    role:get_dict_int('@max_role_power').

%% 获取角色进程的名字
get_proc_name({Rid, P, Z}) ->
    list_to_atom(lists:concat(["role_", Rid, "_", P, "_", Z])).


%% 玩家数据还原检测
check_return(Role) ->
    do_check_return(?role_check_return, Role).
do_check_return([], _Role) -> true;
do_check_return([M | T], Role) ->
    case M:check_return(Role) of
        true -> do_check_return(T, Role);
        Err -> Err
    end.